home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 June: Reference Library / Dev.CD Jun 96 RL / Dev.CD Jun 96 RL.toast / Technical Documentation / develop / develop Issue 24 / develop Issue 24 code / Scriptable Database 1.0a15 / Database / DataRecord.cp < prev    next >
Encoding:
Text File  |  1996-02-19  |  16.2 KB  |  447 lines  |  [TEXT/CWIE]

  1. //================================================================================
  2. // Greg Anderson
  3. // db+
  4. //
  5. // Cursor to a data record
  6. // 19 May 1995
  7. //================================================================================
  8. #pragma once
  9.  
  10. #include "DataRecord.h"
  11.  
  12. #include "DBProperty.h"
  13. #include "GroupControlObject.h"
  14. #include "DatabaseDocument.h"
  15. #include "Transaction.h"
  16.  
  17. //
  18. // For CopyMemory
  19. //
  20. #include "AbstractData.h"
  21.  
  22. #include "Exceptions.h"
  23.  
  24. #define INHERITED TAbstractRecord
  25.  
  26.  
  27. //--------------------------------------------------------------------------------
  28. // TDataRecord::~TDataRecord
  29. //--------------------------------------------------------------------------------
  30. TDataRecord::~TDataRecord()
  31. {
  32. }
  33.  
  34. //--------------------------------------------------------------------------------
  35. // TDataRecord::PrepareToCommit
  36. //--------------------------------------------------------------------------------
  37. void TDataRecord::PrepareToCommit(TTransaction* t)
  38. {
  39.     //
  40.     // If our owner freed us, then mark this record as
  41.     // unused so that it will be pushed onto the free
  42.     // list when the transaction changes are committed.
  43.     //
  44.     if(fDataReleased == true)
  45.     {
  46.         this->FreeThisRecord(t);
  47.     }
  48.     //
  49.     // Check to see if the physical size of our data block
  50.     // has changed, and if so, allocate a new one.
  51.     //
  52.     // Remember, we need to be able to back out
  53.     // the changes made here, just in case another
  54.     // object fails in PrepareToCommit.  Fortunately,
  55.     // this happens automatically, as the relocated
  56.     // data block is simply added to this transaction.
  57.     //
  58.     // n.b. The relocated data will also be given
  59.     // a chance to prepare to commit, but it should
  60.     // discover that its size has not changed, and
  61.     // therefore it will not try to relocate itself
  62.     // again.
  63.     //
  64.     else
  65.     {
  66.         //
  67.         // How big did we use to be, and how big are we now?
  68.         //
  69.         long currentEncodedPhysicalSize = this->GroupControlObject()->BlockEncodedPhysicalSize(this->RecordIndex());
  70.         long desiredEncodedPhysicalSize = this->BlockEncodedPhysicalSize(t);
  71.         long desiredSizeDelta = desiredEncodedPhysicalSize - currentEncodedPhysicalSize;
  72.         
  73.         //
  74.         // If we are _increasing_ in size, or if we are _decreasing_
  75.         // by more than one record in size, then we want to realocate.
  76.         // (We could also realocate if we decrease by only one
  77.         // record, but note then that the free list (kLastDataBlockFreeList - 1)
  78.         // is never used.)
  79.         //
  80.         if((desiredSizeDelta > 0) || (desiredSizeDelta < -1))
  81.         {
  82.             //
  83.             // Make a new data record to store our data in.
  84.             // This record will become part of the transaction,
  85.             // so it will be backed out or committed along with
  86.             // all of the other changes.
  87.             //
  88.             // Note that this new object is added to the beginning
  89.             // of the transaction list, so it will not have a chance
  90.             // to prepare to commit (actually, the code is written
  91.             // under the assumption that PrepareToCommit may or may
  92.             // not be called for the new object).
  93.             //
  94.             AnUpdate<TDataRecord> relocatedData = this->DBDocument()->NewDataRecord(t, this->GetDataLength(t));
  95.             relocatedData->SetDataOwner(t, this->Transaction()->GetDBPropertyUpdatePointer(fDataOwner));
  96.             fDataOwner = AConst<TDBProperty>(nil);
  97.             
  98.             //
  99.             // Require that our data will fit inside the new 
  100.             // record, then copy it over.
  101.             //
  102.             Require(relocatedData->MaximumDataLength(t) >= this->GetDataLength(t));
  103.             relocatedData->SetTypedData(t, this->DataReference(t));
  104.     
  105.             //
  106.             // Now that we've copied our data over, we must
  107.             // release our data.  We'd better put our physical
  108.             // size back to what it was, or we'll may have problems
  109.             // when the free record is committed
  110.             //
  111.             // Everything will come back to the way it was if the transaction
  112.             // changes are discarded, though.
  113.             //
  114.             this->SetBlockEncodedPhysicalSize(t, currentEncodedPhysicalSize);
  115.             this->FreeThisRecord(t);        
  116.         }
  117.     }
  118.     
  119.     INHERITED::PrepareToCommit(t);
  120. } // TDataRecord::PrepareToCommit
  121.  
  122. //--------------------------------------------------------------------------------
  123. // TDataRecord::CommitChanges
  124. //--------------------------------------------------------------------------------
  125. void TDataRecord::CommitChanges(TTransaction* t)
  126. {
  127.     //
  128.     // PrepareToCommit should have made sure that our
  129.     // block size is exactly correct, but in some instances
  130.     // we may need to nudge it a bit
  131.     //
  132.     long currentEncodedPhysicalSize = this->GroupControlObject()->BlockEncodedPhysicalSize(this->RecordIndex());
  133.     long desiredEncodedPhysicalSize = this->BlockEncodedPhysicalSize(t);
  134.     
  135.     //
  136.     // n.b we can't fail in CommitChanges, so we'd better try to
  137.     // make the data fit somehow, some way
  138.     //
  139.     if(currentEncodedPhysicalSize != desiredEncodedPhysicalSize)
  140.     {
  141.         //
  142.         // Big problems will result if our encoded size is wrong
  143.         //
  144.         this->SetBlockEncodedPhysicalSize(t, currentEncodedPhysicalSize);
  145.         
  146.         //
  147.         // If we made the block bigger, make our size correction
  148.         // bigger so that our logical size doesn't change.
  149.         // If we made our block SMALLER (bad news!), then we
  150.         // just clipped some of our data off.  Make our size
  151.         // correction zero to minimize the loss (this should
  152.         // never happen)
  153.         //
  154.         long newSizeCorrection = this->SizeCorrection(t) + ((currentEncodedPhysicalSize - desiredEncodedPhysicalSize) * kSingleRecordSize);
  155.         if(newSizeCorrection < 0)
  156.         {
  157.             ASSERT(false);
  158.             newSizeCorrection = 0;
  159.         }
  160.         this->SetSizeCorrection(t, newSizeCorrection);
  161.     }
  162.     
  163.     //
  164.     // Now that our block is the same size as the slot we'd
  165.     // like to copy it into, go ahead and move it in.
  166.     //
  167.     INHERITED::CommitChanges(t);
  168. } // TDataRecord::CommitChanges
  169.  
  170. //--------------------------------------------------------------------------------
  171. // TDataRecord::ReleaseData
  172. //
  173. // We don't actually convert this record into a free node until it's time
  174. // to commit; that way, we don't need to worry about un-freeing the node or
  175. // allocating a new one if the same property record wants to store more 
  176. // external data in us even after we were freed.
  177. //--------------------------------------------------------------------------------
  178. void TDataRecord::ReleaseData(TTransaction*)
  179. {
  180.     fDataReleased = true;
  181. } // TDataRecord::ReleaseData
  182.  
  183. //--------------------------------------------------------------------------------
  184. // TDataRecord::GetDataType
  185. //
  186. // External data records don't know their data type--they have to ask
  187. // their owner.
  188. //--------------------------------------------------------------------------------
  189. long TDataRecord::GetDataType(TTransaction* t) const
  190. {
  191.     if(fDataOwner.Exists())
  192.         return fDataOwner->GetDataType(t);
  193.     else
  194.         return 0;
  195. }
  196.  
  197. //--------------------------------------------------------------------------------
  198. // TDataRecord::GetDataLength
  199. //
  200. // External data records do know their length, though; it's stored as a
  201. // size-correction off of the block's physical size
  202. //--------------------------------------------------------------------------------
  203. long TDataRecord::GetDataLength(TTransaction* t) const
  204. {
  205.     return this->MaximumDataLength(t) - this->SizeCorrection(t);
  206. }
  207.  
  208. //--------------------------------------------------------------------------------
  209. // TDataRecord::BlockEncodedPhysicalSize
  210. //--------------------------------------------------------------------------------
  211. long TDataRecord::BlockEncodedPhysicalSize(TTransaction* t) const
  212. {
  213.     return (this->GetRecordData(t, kDataFlags) & kDataRecordPhysicalSizeBits) >> kDataRecordPhysicalSizeShift;
  214. }
  215.  
  216. //--------------------------------------------------------------------------------
  217. // TDataRecord::BlockPhysicalSize
  218. //--------------------------------------------------------------------------------
  219. long TDataRecord::BlockPhysicalSize(TTransaction* t) const
  220. {
  221.     return ((this->BlockEncodedPhysicalSize(t) + 1) * (kSingleRecordSize));
  222. }
  223.  
  224. //--------------------------------------------------------------------------------
  225. // TDataRecord::MaximumDataLength
  226. //--------------------------------------------------------------------------------
  227. long TDataRecord::MaximumDataLength(TTransaction* t) const
  228. {
  229.     //
  230.     // We have enough room to store 8 bytes less than our physical size
  231.     // (since our header is two longwords in size)
  232.     //
  233.     return this->BlockPhysicalSize(t) - kHeaderSize;
  234. }
  235.  
  236. //--------------------------------------------------------------------------------
  237. // TDataRecord::SizeCorrection
  238. //--------------------------------------------------------------------------------
  239. long TDataRecord::SizeCorrection(TTransaction* t) const
  240. {
  241.     return (this->GetRecordData(t, kDataFlags) & kDataBlockSizeCorrection) >> kDataBlockSizeCorrectionShift;
  242. }
  243.  
  244. //--------------------------------------------------------------------------------
  245. // TDataRecord::SetSizeCorrection
  246. //--------------------------------------------------------------------------------
  247. void TDataRecord::SetSizeCorrection(TTransaction* t, long sizeCorrection)
  248. {
  249.     long oldFlagValue = this->GetRecordData(t, kDataFlags);
  250.     long newFlagValue = (oldFlagValue & ~kDataBlockSizeCorrection) + (sizeCorrection << kDataBlockSizeCorrectionShift);
  251.  
  252.     this->ChangeRecordData(t, kDataFlags, newFlagValue);
  253. }
  254.  
  255. //--------------------------------------------------------------------------------
  256. // TDataRecord::SetBlockEncodedPhysicalSize
  257. //--------------------------------------------------------------------------------
  258. void TDataRecord::SetBlockEncodedPhysicalSize(TTransaction* t, long blockPhysicalSize)
  259. {
  260.     long oldFlagValue = this->GetRecordData(t, kDataFlags);
  261.     long newFlagValue = (oldFlagValue & ~kDataRecordPhysicalSizeBits) + (blockPhysicalSize << kDataRecordPhysicalSizeShift);
  262.  
  263.     this->ChangeRecordData(t, kDataFlags, newFlagValue);
  264. }
  265.  
  266. //--------------------------------------------------------------------------------
  267. // TDataRecord::SetLogicalSize
  268. //--------------------------------------------------------------------------------
  269. void TDataRecord::SetLogicalSize(TTransaction* t, long logicalSize)
  270. {
  271.     long sizeCorrection = this->MaximumDataLength(t) - logicalSize;
  272.     Require((sizeCorrection >= 0) && (logicalSize >= 0));
  273.     
  274.     //
  275.     // If the size correction is too large, then shrink the
  276.     // physical size of this block down some
  277.     //
  278.     if(sizeCorrection >= kSingleRecordSize)
  279.     {
  280.         long decreaseEncodedSize = sizeCorrection / (kSingleRecordSize);
  281.         sizeCorrection -= (decreaseEncodedSize * (kSingleRecordSize));
  282.         this->SetBlockEncodedPhysicalSize(t, this->BlockEncodedPhysicalSize(t) - decreaseEncodedSize);
  283.     }
  284.     this->SetSizeCorrection(t, sizeCorrection);
  285.     ASSERT(logicalSize == this->GetDataLength(t));
  286. } // TDataRecord::SetLogicalSize
  287.  
  288. //--------------------------------------------------------------------------------
  289. // TDataRecord::GetTypedData
  290. //--------------------------------------------------------------------------------
  291. void TDataRecord::GetTypedData(TTransaction* t, TAbstractDataReference& data) const
  292. {
  293.     data.CopyFrom(this->DataReference(t));
  294. }
  295.  
  296. //--------------------------------------------------------------------------------
  297. // TDataRecord::Verify
  298. //--------------------------------------------------------------------------------
  299. void TDataRecord::Verify(TTransaction*, Boolean /*verifyDeep = false*/) const
  300. {
  301.     //
  302.     // Ask the group control object to verify the next/previous links
  303.     // of this data record
  304.     //
  305.     this->GroupControlObject()->Verify(this->RecordIndex(), false);
  306. }
  307.  
  308. //--------------------------------------------------------------------------------
  309. // TDataRecord::SetDataOwner
  310. //--------------------------------------------------------------------------------
  311. void TDataRecord::SetDataOwner(TTransaction* t, AnUpdate<TDBProperty> dataOwner)
  312. {
  313.     fDataOwner = dataOwner;
  314.     if(dataOwner.Exists())
  315.     {
  316.         this->ChangeRecordData(t, kDataOwnerReference, dataOwner->RecordIndex());
  317.         dataOwner->SetExternalDataIndex(t, this->RecordIndex());
  318.     }
  319. }
  320.  
  321. //--------------------------------------------------------------------------------
  322. // TDataRecord::InitializeNewDataRecord
  323. //--------------------------------------------------------------------------------
  324. void TDataRecord::InitializeNewDataRecord(TTransaction* t)
  325. {
  326.     long oldFlagValue = this->GetRecordData(t, kDataFlags);
  327.     long newFlagValue = oldFlagValue & kInitialDataBlockFlagsMask;
  328.  
  329.     this->ChangeRecordData(t, kDataFlags, newFlagValue);
  330.     this->ChangeRecordData(t, kDataOwnerReference, kNilIndex);
  331. } // TDataRecord::InitializeNewDataRecord
  332.  
  333. //--------------------------------------------------------------------------------
  334. // TDataRecord::SetTypedData
  335. //
  336. // This is the key routine--we need to handle our data block growing larger
  337. // than we have room to store (faked out by making the change image larger,
  338. // and then allocating a different data record when it's time to commit) or
  339. // smaller than we'd like to keep around (handled similarly).
  340. //--------------------------------------------------------------------------------
  341. void TDataRecord::SetTypedData(TTransaction* t, const TAbstractDataReference& data)
  342. {
  343.     //
  344.     // If our data is released by our owner at one point, and
  345.     // new data is given to us later, then set     fDataReleased
  346.     // to false
  347.     //
  348.     if(fDataReleased == true)
  349.     {
  350.         fDataReleased = false;
  351.  
  352.         //
  353.         // This work is probably redundant with a step done
  354.         // later.
  355.         //
  356.         if(fDataOwner.Exists())
  357.             this->Transaction()->GetDBPropertyUpdatePointer(fDataOwner)->SetExternalDataIndex(t, this->RecordIndex());
  358.     }
  359.     
  360.     //
  361.     // If we don't have a change image yet, then make one
  362.     //
  363.     if(fChangeImage == nil)
  364.     {
  365.         fChangeImage = this->GroupControlObject()->MakeRecordDataCopy(this->RecordIndex());
  366.         fChangeImageUnchanged = true;
  367.         fChangeImageMayHaveChangedBack = false;
  368.     }
  369.  
  370.     //
  371.     // It is possible that TAbstractRecord::ChangeRecordData
  372.     // may have created the change image, so we need to initialize
  373.     // the change image length variable independantly of
  374.     // the creation of the change image, above.
  375.     //
  376.     if(fChangeImageSize == 0)
  377.         fChangeImageSize = this->BlockPhysicalSize(t);
  378.  
  379.     //
  380.     // Calculate the minimum size required to fit
  381.     // the provided data into a data record (i.e.,
  382.     // round up the data + headersize to the nearest 32-byte
  383.     // boundary)
  384.     //
  385.     long minEncodedSize = (((data.DataLength() + kHeaderSize) - 1) / (kSingleRecordSize));
  386.     long minLength = (minEncodedSize + 1) * (kSingleRecordSize);
  387.  
  388.     //
  389.     // If the current size of the data record is too small to
  390.     // hold the new data, then ask the group control object
  391.     // to make our change image bigger
  392.     //
  393.     if(minLength > fChangeImageSize)
  394.     {
  395.         long* newChangeImage = new long[minLength / sizeof(long)];
  396.         CopyMemory(fChangeImage, newChangeImage, fChangeImageSize); // memcpy(newChangeImage, fChangeImage, fChangeImageSize);
  397.         delete [] fChangeImage;
  398.         fChangeImage = newChangeImage;
  399.         fChangeImageSize = minLength;
  400.     }
  401.     
  402.     //
  403.     // Set the block encoded physical size to be the
  404.     // smallest value that is >= what is needed to
  405.     // store the data and header.  If the length of
  406.     // our data is shrinking, we will let the change
  407.     // image remain in its too-large state until the
  408.     // transaction changes are committed or discarded.
  409.     //
  410.     // Note that it is somewhat bogus to ever change
  411.     // our encoded block size, since the group control
  412.     // object will not allow us to grow larger or shrink
  413.     // smaller.  However, we will notice the change in
  414.     // size immediately before doing a commit (in
  415.     // PrepareToCommit, above) and copy the data somewhere
  416.     // else.  Of course, if changes are discarded it does
  417.     // not matter what we set our length to, since
  418.     // everything will go back to being the way it was
  419.     // regardless.
  420.     //
  421.     this->SetBlockEncodedPhysicalSize(t, minEncodedSize);
  422.     
  423.     //
  424.     // Copy the new data into the change image, and set the
  425.     // cached value of the current length to an appropriate
  426.     // number (The data length may shrink beyond our ability
  427.     // to represent it with the size correction, or grow beyond
  428.     // the physical size of this block.  Adjusting the actual
  429.     // size of the record is not done until the transaction
  430.     // changes are committed.) 
  431.     //
  432.     TUpdataDataReference updateChangeImage(this->GetDataType(t), (char*) &fChangeImage[kDataStorageStart], this->GetDataLength(t), this->MaximumDataLength(t));
  433.     updateChangeImage.CopyFrom(data);
  434.     this->SetLogicalSize(t, updateChangeImage.DataLength());
  435.     fChangeImageUnchanged = false;
  436.     fChangeImageMayHaveChangedBack = true;
  437. }
  438.  
  439. //--------------------------------------------------------------------------------
  440. // TDataRecord::DataReference
  441. //--------------------------------------------------------------------------------
  442. TConstDataReference TDataRecord::DataReference(TTransaction* t) const
  443. {
  444.     return this->RecordDataReference(t, this->GetDataType(t), kDataStorageStart, this->GetDataLength(t));
  445. }
  446.  
  447.